/*
 * Events - All the events that can be triggered within an FluentListView.
 *
 * Author: Phillip Piper
 * Date: 17/10/2008 9:15 PM
 *
 * Change log:
 * v2.8.0
 * 2014-05-20   JPP  - Added IsHyperlinkEventArgs.IsHyperlink 
 * v2.6
 * 2012-04-17   JPP  - Added group state change and group expansion events
 * v2.5
 * 2010-08-08   JPP  - CellEdit validation and finish events now have NewValue property.
 * v2.4
 * 2010-03-04   JPP  - Added filtering events
 * v2.3
 * 2009-08-16   JPP  - Added group events
 * 2009-08-08   JPP  - Added HotItem event
 * 2009-07-24   JPP  - Added Hyperlink events
 *                   - Added Formatting events
 * v2.2.1
 * 2009-06-13   JPP  - Added Cell events
 *                   - Moved all event parameter blocks to this file.
 *                   - Added Handled property to AfterSearchEventArgs
 * v2.2
 * 2009-06-01   JPP  - Added ColumnToGroupBy and GroupByOrder to sorting events
                     - Gave all event descriptions
 * 2009-04-23   JPP  - Added drag drop events
 * v2.1
 * 2009-01-18   JPP  - Moved SelectionChanged event to this file
 * v2.0
 * 2008-12-06   JPP  - Added searching events
 * 2008-12-01   JPP  - Added secondary sort information to Before/AfterSorting events
 * 2008-10-17   JPP  - Separated from FluentListView.cs
 * 
 * Copyright (C) 2006-2014 Phillip Piper
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using Fluent.Lists;

namespace Fluent.Lists {
	public partial class AdvancedListView {
		//-----------------------------------------------------------------------------------

		#region Events

		/// <summary>
		/// Triggered after a FluentListView has been searched by the user typing into the list
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered after the control has done a search-by-typing action.")]
		public event EventHandler<AfterSearchingEventArgs> AfterSearching;

		/// <summary>
		/// Triggered after a FluentListView has been sorted
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered after the items in the list have been sorted.")]
		public event EventHandler<AfterSortingEventArgs> AfterSorting;

		/// <summary>
		/// Triggered before a FluentListView is searched by the user typing into the list
		/// </summary>
		/// <remarks>
		/// Set Cancelled to true to prevent the searching from taking place.
		/// Changing StringToFind or StartSearchFrom will change the subsequent search.
		/// </remarks>
		[Category("FluentListView")]
		[Description("This event is triggered before the control does a search-by-typing action.")]
		public event EventHandler<BeforeSearchingEventArgs> BeforeSearching;

		/// <summary>
		/// Triggered before a FluentListView is sorted
		/// </summary>
		/// <remarks>
		/// Set Cancelled to true to prevent the sort from taking place.
		/// Changing ColumnToSort or SortOrder will change the subsequent sort.
		/// </remarks>
		[Category("FluentListView")]
		[Description("This event is triggered before the items in the list are sorted.")]
		public event EventHandler<BeforeSortingEventArgs> BeforeSorting;

		/// <summary>
		/// Triggered after a FluentListView has created groups
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered after the groups are created.")]
		public event EventHandler<CreateGroupsEventArgs> AfterCreatingGroups;

		/// <summary>
		/// Triggered before a FluentListView begins to create groups
		/// </summary>
		/// <remarks>
		/// Set Groups to prevent the default group creation process
		/// </remarks>
		[Category("FluentListView")]
		[Description("This event is triggered before the groups are created.")]
		public event EventHandler<CreateGroupsEventArgs> BeforeCreatingGroups;

		/// <summary>
		/// Triggered just before a FluentListView creates groups
		/// </summary>
		/// <remarks>
		/// You can make changes to the groups, which have been created, before those
		/// groups are created within the listview.
		/// </remarks>
		[Category("FluentListView")]
		[Description("This event is triggered when the groups are just about to be created.")]
		public event EventHandler<CreateGroupsEventArgs> AboutToCreateGroups;

		/// <summary>
		/// Triggered when a button in a cell is left clicked.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the user left clicks a button.")]
		public event EventHandler<CellClickEventArgs> ButtonClick;

		/// <summary>
		/// This event is triggered when the user moves a drag over an FluentListView that
		/// has a SimpleDropSink installed as the drop handler.
		/// </summary>
		/// <remarks>
		/// Handlers for this event should set the Effect argument and optionally the
		/// InfoMsg property. They can also change any of the DropTarget* setttings to change
		/// the target of the drop.
		/// </remarks>
		[Category("FluentListView")]
		[Description("Can the user drop the currently dragged items at the current mouse location?")]
		public event EventHandler<OlvDropEventArgs> CanDrop;

		/// <summary>
		/// Triggered when a cell has finished being edited.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered cell edit operation has completely finished")]
		public event CellEditEventHandler CellEditFinished;

		/// <summary>
		/// Triggered when a cell is about to finish being edited.
		/// </summary>
		/// <remarks>If Cancel is already true, the user is cancelling the edit operation.
		/// Set Cancel to true to prevent the value from the cell being written into the model.
		/// You cannot prevent the editing from finishing within this event -- you need
		/// the CellEditValidating event for that.</remarks>
		[Category("FluentListView")]
		[Description("This event is triggered cell edit operation is finishing.")]
		public event CellEditEventHandler CellEditFinishing;

		/// <summary>
		/// Triggered when a cell is about to be edited.
		/// </summary>
		/// <remarks>Set Cancel to true to prevent the cell being edited.
		/// You can change the the Control to be something completely different.</remarks>
		[Category("FluentListView")]
		[Description("This event is triggered when cell edit is about to begin.")]
		public event CellEditEventHandler CellEditStarting;

		/// <summary>
		/// Triggered when a cell editor needs to be validated
		/// </summary>
		/// <remarks>
		/// If this event is cancelled, focus will remain on the cell editor.
		/// </remarks>
		[Category("FluentListView")]
		[Description("This event is triggered when a cell editor is about to lose focus and its new contents need to be validated.")]
		public event CellEditEventHandler CellEditValidating;

		/// <summary>
		/// Triggered when a cell is left clicked.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the user left clicks a cell.")]
		public event EventHandler<CellClickEventArgs> CellClick;

		/// <summary>
		/// Triggered when the mouse is above a cell.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the mouse is over a cell.")]
		public event EventHandler<CellOverEventArgs> CellOver;

		/// <summary>
		/// Triggered when a cell is right clicked.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the user right clicks a cell.")]
		public event EventHandler<CellRightClickEventArgs> CellRightClick;

		/// <summary>
		/// This event is triggered when a cell needs a tool tip.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a cell needs a tool tip.")]
		public event EventHandler<ToolTipShowingEventArgs> CellToolTipShowing;

		/// <summary>
		/// This event is triggered when a checkbox is checked/unchecked on a subitem
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a checkbox is checked/unchecked on a subitem.")]
		public event EventHandler<SubItemCheckingEventArgs> SubItemChecking;

		/// <summary>
		/// Triggered when a column header is right clicked.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the user right clicks a column header.")]
		public event ColumnRightClickEventHandler ColumnRightClick;

		/// <summary>
		/// This event is triggered when the user releases a drag over an FluentListView that
		/// has a SimpleDropSink installed as the drop handler.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the user dropped items onto the control.")]
		public event EventHandler<OlvDropEventArgs> Dropped;

		/// <summary>
		/// This event is triggered when the control needs to filter its collection of objects.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the control needs to filter its collection of objects.")]
		public event EventHandler<FilterEventArgs> Filter;

		/// <summary>
		/// This event is triggered when a cell needs to be formatted.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a cell needs to be formatted.")]
		public event EventHandler<FormatCellEventArgs> FormatCell;

		/// <summary>
		/// This event is triggered when the frozeness of the control changes.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when frozeness of the control changes.")]
		public event EventHandler<FreezeEventArgs> Freezing;

		/// <summary>
		/// This event is triggered when a row needs to be formatted.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a row needs to be formatted.")]
		public event EventHandler<FormatRowEventArgs> FormatRow;

		/// <summary>
		/// This event is triggered when a group is about to collapse or expand.
		/// This can be cancelled to prevent the expansion.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a group is about to collapse or expand.")]
		public event EventHandler<GroupExpandingCollapsingEventArgs> GroupExpandingCollapsing;

		/// <summary>
		/// This event is triggered when a group changes state.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a group changes state.")]
		public event EventHandler<GroupStateChangedEventArgs> GroupStateChanged;

		/// <summary>
		/// This event is triggered when a header checkbox is changing value
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a header checkbox changes value.")]
		public event EventHandler<HeaderCheckBoxChangingEventArgs> HeaderCheckBoxChanging;

		/// <summary>
		/// This event is triggered when a header needs a tool tip.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a header needs a tool tip.")]
		public event EventHandler<ToolTipShowingEventArgs> HeaderToolTipShowing;

		/// <summary>
		/// Triggered when the "hot" item changes
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the hot item changed.")]
		public event EventHandler<HotItemChangedEventArgs> HotItemChanged;

		/// <summary>
		/// Triggered when a hyperlink cell is clicked.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a hyperlink cell is clicked.")]
		public event EventHandler<HyperlinkClickedEventArgs> HyperlinkClicked;

		/// <summary>
		/// Triggered when the task text of a group is clicked.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the task text of a group is clicked.")]
		public event EventHandler<GroupTaskClickedEventArgs> GroupTaskClicked;

		/// <summary>
		/// Is the value in the given cell a hyperlink.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the control needs to know if a given cell contains a hyperlink.")]
		public event EventHandler<IsHyperlinkEventArgs> IsHyperlink;

		/// <summary>
		/// Some new objects are about to be added to an FluentListView.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when objects are about to be added to the control")]
		public event EventHandler<ItemsAddingEventArgs> ItemsAdding;

		/// <summary>
		/// The contents of the FluentListView has changed.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the contents of the control have changed.")]
		public event EventHandler<ItemsChangedEventArgs> ItemsChanged;

		/// <summary>
		/// The contents of the FluentListView is about to change via a SetObjects call
		/// </summary>
		/// <remarks>
		/// <para>Set Cancelled to true to prevent the contents of the list changing. This does not work with virtual lists.</para>
		/// </remarks>
		[Category("FluentListView")]
		[Description("This event is triggered when the contents of the control changes.")]
		public event EventHandler<ItemsChangingEventArgs> ItemsChanging;

		/// <summary>
		/// Some objects are about to be removed from an FluentListView.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when objects are removed from the control.")]
		public event EventHandler<ItemsRemovingEventArgs> ItemsRemoving;

		/// <summary>
		/// This event is triggered when the user moves a drag over an FluentListView that
		/// has a SimpleDropSink installed as the drop handler, and when the source control
		/// for the drag was an FluentListView.
		/// </summary>
		/// <remarks>
		/// Handlers for this event should set the Effect argument and optionally the
		/// InfoMsg property. They can also change any of the DropTarget* setttings to change
		/// the target of the drop.
		/// </remarks>
		[Category("FluentListView")]
		[Description("Can the dragged collection of model objects be dropped at the current mouse location")]
		public event EventHandler<ModelDropEventArgs> ModelCanDrop;

		/// <summary>
		/// This event is triggered when the user releases a drag over an FluentListView that
		/// has a SimpleDropSink installed as the drop handler and when the source control
		/// for the drag was an FluentListView.
		/// </summary>
		[Category("FluentListView")]
		[Description("A collection of model objects from a FluentListView has been dropped on this control")]
		public event EventHandler<ModelDropEventArgs> ModelDropped;

		/// <summary>
		/// This event is triggered once per user action that changes the selection state
		/// of one or more rows.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered once per user action that changes the selection state of one or more rows.")]
		public event EventHandler SelectionChanged;

		/// <summary>
		/// This event is triggered when the contents of the FluentListView has scrolled.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when the contents of the FluentListView has scrolled.")]
		public event EventHandler<ScrollEventArgs> Scroll;

		#endregion

		//-----------------------------------------------------------------------------------

		#region OnEvents

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnAboutToCreateGroups(CreateGroupsEventArgs e) {
			if (AboutToCreateGroups != null) {
				AboutToCreateGroups(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnBeforeCreatingGroups(CreateGroupsEventArgs e) {
			if (BeforeCreatingGroups != null) {
				BeforeCreatingGroups(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnAfterCreatingGroups(CreateGroupsEventArgs e) {
			if (AfterCreatingGroups != null) {
				AfterCreatingGroups(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnAfterSearching(AfterSearchingEventArgs e) {
			if (AfterSearching != null) {
				AfterSearching(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnAfterSorting(AfterSortingEventArgs e) {
			if (AfterSorting != null) {
				AfterSorting(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnBeforeSearching(BeforeSearchingEventArgs e) {
			if (BeforeSearching != null) {
				BeforeSearching(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnBeforeSorting(BeforeSortingEventArgs e) {
			if (BeforeSorting != null) {
				BeforeSorting(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnButtonClick(CellClickEventArgs args) {
			if (ButtonClick != null) {
				ButtonClick(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnCanDrop(OlvDropEventArgs args) {
			if (CanDrop != null) {
				CanDrop(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnCellClick(CellClickEventArgs args) {
			if (CellClick != null) {
				CellClick(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnCellOver(CellOverEventArgs args) {
			if (CellOver != null) {
				CellOver(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnCellRightClick(CellRightClickEventArgs args) {
			if (CellRightClick != null) {
				CellRightClick(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnCellToolTip(ToolTipShowingEventArgs args) {
			if (CellToolTipShowing != null) {
				CellToolTipShowing(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnSubItemChecking(SubItemCheckingEventArgs args) {
			if (SubItemChecking != null) {
				SubItemChecking(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnColumnRightClick(ColumnClickEventArgs e) {
			if (ColumnRightClick != null) {
				ColumnRightClick(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnDropped(OlvDropEventArgs args) {
			if (Dropped != null) {
				Dropped(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected internal virtual void OnFilter(FilterEventArgs e) {
			if (Filter != null) {
				Filter(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnFormatCell(FormatCellEventArgs args) {
			if (FormatCell != null) {
				FormatCell(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnFormatRow(FormatRowEventArgs args) {
			if (FormatRow != null) {
				FormatRow(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnFreezing(FreezeEventArgs args) {
			if (Freezing != null) {
				Freezing(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnGroupExpandingCollapsing(GroupExpandingCollapsingEventArgs args) {
			if (GroupExpandingCollapsing != null) {
				GroupExpandingCollapsing(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnGroupStateChanged(GroupStateChangedEventArgs args) {
			if (GroupStateChanged != null) {
				GroupStateChanged(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnHeaderCheckBoxChanging(HeaderCheckBoxChangingEventArgs args) {
			if (HeaderCheckBoxChanging != null) {
				HeaderCheckBoxChanging(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnHeaderToolTip(ToolTipShowingEventArgs args) {
			if (HeaderToolTipShowing != null) {
				HeaderToolTipShowing(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnHotItemChanged(HotItemChangedEventArgs e) {
			if (HotItemChanged != null) {
				HotItemChanged(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnHyperlinkClicked(HyperlinkClickedEventArgs e) {
			if (HyperlinkClicked != null) {
				HyperlinkClicked(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnGroupTaskClicked(GroupTaskClickedEventArgs e) {
			if (GroupTaskClicked != null) {
				GroupTaskClicked(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnIsHyperlink(IsHyperlinkEventArgs e) {
			if (IsHyperlink != null) {
				IsHyperlink(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnItemsAdding(ItemsAddingEventArgs e) {
			if (ItemsAdding != null) {
				ItemsAdding(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnItemsChanged(ItemsChangedEventArgs e) {
			if (ItemsChanged != null) {
				ItemsChanged(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnItemsChanging(ItemsChangingEventArgs e) {
			if (ItemsChanging != null) {
				ItemsChanging(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnItemsRemoving(ItemsRemovingEventArgs e) {
			if (ItemsRemoving != null) {
				ItemsRemoving(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnModelCanDrop(ModelDropEventArgs args) {
			if (ModelCanDrop != null) {
				ModelCanDrop(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnModelDropped(ModelDropEventArgs args) {
			if (ModelDropped != null) {
				ModelDropped(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnSelectionChanged(EventArgs e) {
			if (SelectionChanged != null) {
				SelectionChanged(this, e);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnScroll(ScrollEventArgs e) {
			if (Scroll != null) {
				Scroll(this, e);
			}
		}


		/// <summary>
		/// Tell the world when a cell is about to be edited.
		/// </summary>
		protected virtual void OnCellEditStarting(CellEditEventArgs e) {
			if (CellEditStarting != null) {
				CellEditStarting(this, e);
			}
		}

		/// <summary>
		/// Tell the world when a cell is about to finish being edited.
		/// </summary>
		protected virtual void OnCellEditorValidating(CellEditEventArgs e) {
			// Hack. ListView is an imperfect control container. It does not manage validation
			// perfectly. If the ListView is part of a TabControl, and the cell editor loses
			// focus by the user clicking on another tab, the TabControl processes the click
			// and switches tabs, even if this Validating event cancels. This results in the
			// strange situation where the cell editor is active, but isn't visible. When the
			// user switches back to the tab with the ListView, composite controls like spin
			// controls, DateTimePicker and ComboBoxes do not work properly. Specifically,
			// keyboard input still works fine, but the controls do not respond to mouse
			// input. SO, if the validation fails, we have to specifically give focus back to
			// the cell editor. (this is the Select() call in the code below). 
			// But (there is always a 'but'), doing that changes the focus so the cell editor
			// triggers another Validating event -- which fails again. From the user's point
			// of view, they click away from the cell editor, and the validating code
			// complains twice. So we only trigger a Validating event if more than 0.1 seconds
			// has elapsed since the last validate event.
			// I know it's a hack. I'm very open to hear a neater solution.

			// Also, this timed response stops us from sending a series of validation events
			// if the user clicks and holds on the OLV scroll bar.
			//System.Diagnostics.Debug.WriteLine(Environment.TickCount - lastValidatingEvent);
			if (Environment.TickCount - lastValidatingEvent < 100) {
				e.Cancel = true;
			}
			else {
				lastValidatingEvent = Environment.TickCount;
				if (CellEditValidating != null) {
					CellEditValidating(this, e);
				}
			}

			lastValidatingEvent = Environment.TickCount;
		}

		private int lastValidatingEvent = 0;

		/// <summary>
		/// Tell the world when a cell is about to finish being edited.
		/// </summary>
		protected virtual void OnCellEditFinishing(CellEditEventArgs e) {
			if (CellEditFinishing != null) {
				CellEditFinishing(this, e);
			}
		}

		/// <summary>
		/// Tell the world when a cell has finished being edited.
		/// </summary>
		protected virtual void OnCellEditFinished(CellEditEventArgs e) {
			if (CellEditFinished != null) {
				CellEditFinished(this, e);
			}
		}

		#endregion
	}

	public partial class TreeListView {
		#region Events

		/// <summary>
		/// This event is triggered when user input requests the expansion of a list item.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a branch is about to expand.")]
		public event EventHandler<TreeBranchExpandingEventArgs> Expanding;

		/// <summary>
		/// This event is triggered when user input requests the collapse of a list item.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a branch is about to collapsed.")]
		public event EventHandler<TreeBranchCollapsingEventArgs> Collapsing;

		/// <summary>
		/// This event is triggered after the expansion of a list item due to user input.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a branch has been expanded.")]
		public event EventHandler<TreeBranchExpandedEventArgs> Expanded;

		/// <summary>
		/// This event is triggered after the collapse of a list item due to user input.
		/// </summary>
		[Category("FluentListView")]
		[Description("This event is triggered when a branch has been collapsed.")]
		public event EventHandler<TreeBranchCollapsedEventArgs> Collapsed;

		#endregion

		#region OnEvents

		/// <summary>
		/// Trigger the expanding event
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnExpanding(TreeBranchExpandingEventArgs e) {
			if (Expanding != null) {
				Expanding(this, e);
			}
		}

		/// <summary>
		/// Trigger the collapsing event
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnCollapsing(TreeBranchCollapsingEventArgs e) {
			if (Collapsing != null) {
				Collapsing(this, e);
			}
		}

		/// <summary>
		/// Trigger the expanded event
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnExpanded(TreeBranchExpandedEventArgs e) {
			if (Expanded != null) {
				Expanded(this, e);
			}
		}

		/// <summary>
		/// Trigger the collapsed event
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnCollapsed(TreeBranchCollapsedEventArgs e) {
			if (Collapsed != null) {
				Collapsed(this, e);
			}
		}

		#endregion
	}
}

namespace Fluent {
	/// <summary>
	/// The callbacks for CellEditing events
	/// </summary>
	/// <remarks> this 
	/// We could replace this with EventHandler&lt;CellEditEventArgs&gt; but that would break all
	/// cell editing event code from v1.x.
	/// </remarks>
	public delegate void CellEditEventHandler(object sender, CellEditEventArgs e);


	//-----------------------------------------------------------------------------------

	#region Event Parameter Blocks

	/// <summary>
	/// Let the world know that a cell edit operation is beginning or ending
	/// </summary>
	public class CellEditEventArgs : EventArgs {
		/// <summary>
		/// Create an event args
		/// </summary>
		/// <param name="column"></param>
		/// <param name="control"></param>
		/// <param name="cellBounds"></param>
		/// <param name="item"></param>
		/// <param name="subItemIndex"></param>
		public CellEditEventArgs(OLVColumn column, Control control, Rectangle cellBounds, OLVListItem item, int subItemIndex) {
			Control = control;
			this.column = column;
			this.cellBounds = cellBounds;
			listViewItem = item;
			rowObject = item.RowObject;
			this.subItemIndex = subItemIndex;
			value = column.GetValue(item.RowObject);
		}

		/// <summary>
		/// Change this to true to cancel the cell editing operation.
		/// </summary>
		/// <remarks>
		/// <para>During the CellEditStarting event, setting this to true will prevent the cell from being edited.</para>
		/// <para>During the CellEditFinishing event, if this value is already true, this indicates that the user has
		/// cancelled the edit operation and that the handler should perform cleanup only. Setting this to true,
		/// will prevent the FluentListView from trying to write the new value into the model object.</para>
		/// </remarks>
		public bool Cancel;

		/// <summary>
		/// During the CellEditStarting event, this can be modified to be the control that you want
		/// to edit the value. You must fully configure the control before returning from the event,
		/// including its bounds and the value it is showing.
		/// During the CellEditFinishing event, you can use this to get the value that the user
		/// entered and commit that value to the model. Changing the control during the finishing
		/// event has no effect.
		/// </summary>
		public Control Control;

		/// <summary>
		/// The column of the cell that is going to be or has been edited.
		/// </summary>
		public OLVColumn Column => column;

		private OLVColumn column;

		/// <summary>
		/// The model object of the row of the cell that is going to be or has been edited.
		/// </summary>
		public object RowObject => rowObject;

		private object rowObject;

		/// <summary>
		/// The listview item of the cell that is going to be or has been edited.
		/// </summary>
		public OLVListItem ListViewItem => listViewItem;

		private OLVListItem listViewItem;

		/// <summary>
		/// The data value of the cell as it stands in the control.
		/// </summary>
		/// <remarks>Only validate during Validating and Finishing events.</remarks>
		public object NewValue {
			get => newValue;
			set => newValue = value;
		}

		private object newValue;

		/// <summary>
		/// The index of the cell that is going to be or has been edited.
		/// </summary>
		public int SubItemIndex => subItemIndex;

		private int subItemIndex;

		/// <summary>
		/// The data value of the cell before the edit operation began.
		/// </summary>
		public object Value => value;

		private object value;

		/// <summary>
		/// The bounds of the cell that is going to be or has been edited.
		/// </summary>
		public Rectangle CellBounds => cellBounds;

		private Rectangle cellBounds;

		/// <summary>
		/// Gets or sets whether the control used for editing should be auto matically disposed
		/// when the cell edit operation finishes. Defaults to true
		/// </summary>
		/// <remarks>If the control is expensive to create, you might want to cache it and reuse for
		/// for various cells. If so, you don't want FluentListView to dispose of the control automatically</remarks>
		public bool AutoDispose {
			get => autoDispose;
			set => autoDispose = value;
		}

		private bool autoDispose = true;
	}

	/// <summary>
	/// Event blocks for events that can be cancelled
	/// </summary>
	public class CancellableEventArgs : EventArgs {
		/// <summary>
		/// Has this event been cancelled by the event handler?
		/// </summary>
		public bool Canceled;
	}

	/// <summary>
	/// BeforeSorting
	/// </summary>
	public class BeforeSortingEventArgs : CancellableEventArgs {
		/// <summary>
		/// Create BeforeSortingEventArgs
		/// </summary>
		/// <param name="column"></param>
		/// <param name="order"></param>
		/// <param name="column2"></param>
		/// <param name="order2"></param>
		public BeforeSortingEventArgs(OLVColumn column, SortOrder order, OLVColumn column2, SortOrder order2) {
			ColumnToGroupBy = column;
			GroupByOrder = order;
			ColumnToSort = column;
			SortOrder = order;
			SecondaryColumnToSort = column2;
			SecondarySortOrder = order2;
		}

		/// <summary>
		/// Create BeforeSortingEventArgs
		/// </summary>
		/// <param name="groupColumn"></param>
		/// <param name="groupOrder"></param>
		/// <param name="column"></param>
		/// <param name="order"></param>
		/// <param name="column2"></param>
		/// <param name="order2"></param>
		public BeforeSortingEventArgs(OLVColumn groupColumn, SortOrder groupOrder, OLVColumn column, SortOrder order, OLVColumn column2, SortOrder order2) {
			ColumnToGroupBy = groupColumn;
			GroupByOrder = groupOrder;
			ColumnToSort = column;
			SortOrder = order;
			SecondaryColumnToSort = column2;
			SecondarySortOrder = order2;
		}

		/// <summary>
		/// Did the event handler already do the sorting for us?
		/// </summary>
		public bool Handled;

		/// <summary>
		/// What column will be used for grouping
		/// </summary>
		public OLVColumn ColumnToGroupBy;

		/// <summary>
		/// How will groups be ordered
		/// </summary>
		public SortOrder GroupByOrder;

		/// <summary>
		/// What column will be used for sorting
		/// </summary>
		public OLVColumn ColumnToSort;

		/// <summary>
		/// What order will be used for sorting. None means no sorting.
		/// </summary>
		public SortOrder SortOrder;

		/// <summary>
		/// What column will be used for secondary sorting?
		/// </summary>
		public OLVColumn SecondaryColumnToSort;

		/// <summary>
		/// What order will be used for secondary sorting?
		/// </summary>
		public SortOrder SecondarySortOrder;
	}

	/// <summary>
	/// Sorting has just occurred.
	/// </summary>
	public class AfterSortingEventArgs : EventArgs {
		/// <summary>
		/// Create a AfterSortingEventArgs
		/// </summary>
		/// <param name="groupColumn"></param>
		/// <param name="groupOrder"></param>
		/// <param name="column"></param>
		/// <param name="order"></param>
		/// <param name="column2"></param>
		/// <param name="order2"></param>
		public AfterSortingEventArgs(OLVColumn groupColumn, SortOrder groupOrder, OLVColumn column, SortOrder order, OLVColumn column2, SortOrder order2) {
			columnToGroupBy = groupColumn;
			groupByOrder = groupOrder;
			columnToSort = column;
			sortOrder = order;
			secondaryColumnToSort = column2;
			secondarySortOrder = order2;
		}

		/// <summary>
		/// Create a AfterSortingEventArgs
		/// </summary>
		/// <param name="args"></param>
		public AfterSortingEventArgs(BeforeSortingEventArgs args) {
			columnToGroupBy = args.ColumnToGroupBy;
			groupByOrder = args.GroupByOrder;
			columnToSort = args.ColumnToSort;
			sortOrder = args.SortOrder;
			secondaryColumnToSort = args.SecondaryColumnToSort;
			secondarySortOrder = args.SecondarySortOrder;
		}

		/// <summary>
		/// What column was used for grouping?
		/// </summary>
		public OLVColumn ColumnToGroupBy => columnToGroupBy;

		private OLVColumn columnToGroupBy;

		/// <summary>
		/// What ordering was used for grouping?
		/// </summary>
		public SortOrder GroupByOrder => groupByOrder;

		private SortOrder groupByOrder;

		/// <summary>
		/// What column was used for sorting?
		/// </summary>
		public OLVColumn ColumnToSort => columnToSort;

		private OLVColumn columnToSort;

		/// <summary>
		/// What ordering was used for sorting?
		/// </summary>
		public SortOrder SortOrder => sortOrder;

		private SortOrder sortOrder;

		/// <summary>
		/// What column was used for secondary sorting?
		/// </summary>
		public OLVColumn SecondaryColumnToSort => secondaryColumnToSort;

		private OLVColumn secondaryColumnToSort;

		/// <summary>
		/// What order was used for secondary sorting?
		/// </summary>
		public SortOrder SecondarySortOrder => secondarySortOrder;

		private SortOrder secondarySortOrder;
	}

	/// <summary>
	/// This event is triggered when the contents of a list have changed
	/// and we want the world to have a chance to filter the list.
	/// </summary>
	public class FilterEventArgs : EventArgs {
		/// <summary>
		/// Create a FilterEventArgs
		/// </summary>
		/// <param name="objects"></param>
		public FilterEventArgs(IEnumerable objects) {
			Objects = objects;
		}

		/// <summary>
		/// Gets or sets what objects are being filtered
		/// </summary>
		public IEnumerable Objects;

		/// <summary>
		/// Gets or sets what objects survived the filtering
		/// </summary>
		public IEnumerable FilteredObjects;
	}

	/// <summary>
	/// This event is triggered after the items in the list have been changed,
	/// either through SetObjects, AddObjects or RemoveObjects.
	/// </summary>
	public class ItemsChangedEventArgs : EventArgs {
		/// <summary>
		/// Create a ItemsChangedEventArgs
		/// </summary>
		public ItemsChangedEventArgs() {
		}

		/// <summary>
		/// Constructor for this event when used by a virtual list
		/// </summary>
		/// <param name="oldObjectCount"></param>
		/// <param name="newObjectCount"></param>
		public ItemsChangedEventArgs(int oldObjectCount, int newObjectCount) {
			this.oldObjectCount = oldObjectCount;
			this.newObjectCount = newObjectCount;
		}

		/// <summary>
		/// Gets how many items were in the list before it changed
		/// </summary>
		public int OldObjectCount => oldObjectCount;

		private int oldObjectCount;

		/// <summary>
		/// Gets how many objects are in the list after the change.
		/// </summary>
		public int NewObjectCount => newObjectCount;

		private int newObjectCount;
	}

	/// <summary>
	/// This event is triggered by AddObjects before any change has been made to the list.
	/// </summary>
	public class ItemsAddingEventArgs : CancellableEventArgs {
		/// <summary>
		/// Create an ItemsAddingEventArgs
		/// </summary>
		/// <param name="objectsToAdd"></param>
		public ItemsAddingEventArgs(ICollection objectsToAdd) {
			ObjectsToAdd = objectsToAdd;
		}

		/// <summary>
		/// Create an ItemsAddingEventArgs
		/// </summary>
		/// <param name="index"></param>
		/// <param name="objectsToAdd"></param>
		public ItemsAddingEventArgs(int index, ICollection objectsToAdd) {
			Index = index;
			ObjectsToAdd = objectsToAdd;
		}

		/// <summary>
		/// Gets or sets where the collection is going to be inserted.
		/// </summary>
		public int Index;

		/// <summary>
		/// Gets or sets the objects to be added to the list
		/// </summary>
		public ICollection ObjectsToAdd;
	}

	/// <summary>
	/// This event is triggered by SetObjects before any change has been made to the list.
	/// </summary>
	/// <remarks>
	/// When used with a virtual list, OldObjects will always be null.
	/// </remarks>
	public class ItemsChangingEventArgs : CancellableEventArgs {
		/// <summary>
		/// Create ItemsChangingEventArgs
		/// </summary>
		/// <param name="oldObjects"></param>
		/// <param name="newObjects"></param>
		public ItemsChangingEventArgs(IEnumerable oldObjects, IEnumerable newObjects) {
			this.oldObjects = oldObjects;
			NewObjects = newObjects;
		}

		/// <summary>
		/// Gets the objects that were in the list before it change.
		/// For virtual lists, this will always be null.
		/// </summary>
		public IEnumerable OldObjects => oldObjects;

		private IEnumerable oldObjects;

		/// <summary>
		/// Gets or sets the objects that will be in the list after it changes.
		/// </summary>
		public IEnumerable NewObjects;
	}

	/// <summary>
	/// This event is triggered by RemoveObjects before any change has been made to the list.
	/// </summary>
	public class ItemsRemovingEventArgs : CancellableEventArgs {
		/// <summary>
		/// Create an ItemsRemovingEventArgs
		/// </summary>
		/// <param name="objectsToRemove"></param>
		public ItemsRemovingEventArgs(ICollection objectsToRemove) {
			ObjectsToRemove = objectsToRemove;
		}

		/// <summary>
		/// Gets or sets the objects that will be removed
		/// </summary>
		public ICollection ObjectsToRemove;
	}

	/// <summary>
	/// Triggered after the user types into a list
	/// </summary>
	public class AfterSearchingEventArgs : EventArgs {
		/// <summary>
		/// Create an AfterSearchingEventArgs
		/// </summary>
		/// <param name="stringToFind"></param>
		/// <param name="indexSelected"></param>
		public AfterSearchingEventArgs(string stringToFind, int indexSelected) {
			this.stringToFind = stringToFind;
			this.indexSelected = indexSelected;
		}

		/// <summary>
		/// Gets the string that was actually searched for
		/// </summary>
		public string StringToFind => stringToFind;

		private string stringToFind;

		/// <summary>
		/// Gets or sets whether an the event handler already handled this event
		/// </summary>
		public bool Handled;

		/// <summary>
		/// Gets the index of the row that was selected by the search.
		/// -1 means that no row was matched
		/// </summary>
		public int IndexSelected => indexSelected;

		private int indexSelected;
	}

	/// <summary>
	/// Triggered when the user types into a list
	/// </summary>
	public class BeforeSearchingEventArgs : CancellableEventArgs {
		/// <summary>
		/// Create BeforeSearchingEventArgs
		/// </summary>
		/// <param name="stringToFind"></param>
		/// <param name="startSearchFrom"></param>
		public BeforeSearchingEventArgs(string stringToFind, int startSearchFrom) {
			StringToFind = stringToFind;
			StartSearchFrom = startSearchFrom;
		}

		/// <summary>
		/// Gets or sets the string that will be found by the search routine
		/// </summary>
		/// <remarks>Modifying this value does not modify the memory of what the user has typed. 
		/// When the user next presses a character, the search string will revert to what 
		/// the user has actually typed.</remarks>
		public string StringToFind;

		/// <summary>
		/// Gets or sets the index of the first row that will be considered to matching.
		/// </summary>
		public int StartSearchFrom;
	}

	/// <summary>
	/// The parameter block when telling the world about a cell based event
	/// </summary>
	public class CellEventArgs : EventArgs {
		/// <summary>
		/// Gets the FluentListView that is the source of the event
		/// </summary>
		public AdvancedListView ListView {
			get => listView;
			internal set => listView = value;
		}

		private AdvancedListView listView;

		/// <summary>
		/// Gets the model object under the cell
		/// </summary>
		/// <remarks>This is null for events triggered by the header.</remarks>
		public object Model {
			get => model;
			internal set => model = value;
		}

		private object model;

		/// <summary>
		/// Gets the row index of the cell
		/// </summary>
		/// <remarks>This is -1 for events triggered by the header.</remarks>
		public int RowIndex {
			get => rowIndex;
			internal set => rowIndex = value;
		}

		private int rowIndex = -1;

		/// <summary>
		/// Gets the column index of the cell
		/// </summary>
		/// <remarks>This is -1 when the view is not in details view.</remarks>
		public int ColumnIndex {
			get => columnIndex;
			internal set => columnIndex = value;
		}

		private int columnIndex = -1;

		/// <summary>
		/// Gets the column of the cell 
		/// </summary>
		/// <remarks>This is null when the view is not in details view.</remarks>
		public OLVColumn Column {
			get => column;
			internal set => column = value;
		}

		private OLVColumn column;

		/// <summary>
		/// Gets the location of the mouse at the time of the event
		/// </summary>
		public Point Location {
			get => location;
			internal set => location = value;
		}

		private Point location;

		/// <summary>
		/// Gets the state of the modifier keys at the time of the event
		/// </summary>
		public Keys ModifierKeys {
			get => modifierKeys;
			internal set => modifierKeys = value;
		}

		private Keys modifierKeys;

		/// <summary>
		/// Gets the item of the cell
		/// </summary>
		public OLVListItem Item {
			get => item;
			internal set => item = value;
		}

		private OLVListItem item;

		/// <summary>
		/// Gets the subitem of the cell
		/// </summary>
		/// <remarks>This is null when the view is not in details view and 
		/// for event triggered by the header</remarks>
		public OLVListSubItem SubItem {
			get => subItem;
			internal set => subItem = value;
		}

		private OLVListSubItem subItem;

		/// <summary>
		/// Gets the HitTest object that determined which cell was hit
		/// </summary>
		public OlvListViewHitTestInfo HitTest {
			get => hitTest;
			internal set => hitTest = value;
		}

		private OlvListViewHitTestInfo hitTest;

		/// <summary>
		/// Gets or set if this event completelely handled. If it was, no further processing
		/// will be done for it.
		/// </summary>
		public bool Handled;
	}

	/// <summary>
	/// Tells the world that a cell was clicked
	/// </summary>
	public class CellClickEventArgs : CellEventArgs {
		/// <summary>
		/// Gets or sets the number of clicks associated with this event
		/// </summary>
		public int ClickCount {
			get => clickCount;
			set => clickCount = value;
		}

		private int clickCount;
	}

	/// <summary>
	/// Tells the world that a cell was right clicked
	/// </summary>
	public class CellRightClickEventArgs : CellEventArgs {
		/// <summary>
		/// Gets or sets the menu that should be displayed as a result of this event.
		/// </summary>
		/// <remarks>The menu will be positioned at Location, so changing that property changes
		/// where the menu will be displayed.</remarks>
		public ContextMenuStrip MenuStrip;
	}

	/// <summary>
	/// Tell the world that the mouse is over a given cell
	/// </summary>
	public class CellOverEventArgs : CellEventArgs {
	}

	/// <summary>
	/// Tells the world that the frozen-ness of the FluentListView has changed.
	/// </summary>
	public class FreezeEventArgs : EventArgs {
		/// <summary>
		/// Make a FreezeEventArgs
		/// </summary>
		/// <param name="freeze"></param>
		public FreezeEventArgs(int freeze) {
			FreezeLevel = freeze;
		}

		/// <summary>
		/// How frozen is the control? 0 means that the control is unfrozen, 
		/// more than 0 indicates froze.
		/// </summary>
		public int FreezeLevel {
			get => freezeLevel;
			set => freezeLevel = value;
		}

		private int freezeLevel;
	}

	/// <summary>
	/// The parameter block when telling the world that a tool tip is about to be shown.
	/// </summary>
	public class ToolTipShowingEventArgs : CellEventArgs {
		/// <summary>
		/// Gets the tooltip control that is triggering the tooltip event
		/// </summary>
		public ToolTipControl ToolTipControl {
			get => toolTipControl;
			internal set => toolTipControl = value;
		}

		private ToolTipControl toolTipControl;

		/// <summary>
		/// Gets or sets the text should be shown on the tooltip for this event
		/// </summary>
		/// <remarks>Setting this to empty or null prevents any tooltip from showing</remarks>
		public string Text;

		/// <summary>
		/// In what direction should the text for this tooltip be drawn?
		/// </summary>
		public RightToLeft RightToLeft;

		/// <summary>
		/// Should the tooltip for this event been shown in bubble style?
		/// </summary>
		/// <remarks>This doesn't work reliable under Vista</remarks>
		public bool? IsBalloon;

		/// <summary>
		/// What color should be used for the background of the tooltip
		/// </summary>
		/// <remarks>Setting this does nothing under Vista</remarks>
		public Color? BackColor;

		/// <summary>
		/// What color should be used for the foreground of the tooltip
		/// </summary>
		/// <remarks>Setting this does nothing under Vista</remarks>
		public Color? ForeColor;

		/// <summary>
		/// What string should be used as the title for the tooltip for this event?
		/// </summary>
		public string Title;

		/// <summary>
		/// Which standard icon should be used for the tooltip for this event
		/// </summary>
		public ToolTipControl.StandardIcons? StandardIcon;

		/// <summary>
		/// How many milliseconds should the tooltip remain before it automatically
		/// disappears.
		/// </summary>
		public int? AutoPopDelay;

		/// <summary>
		/// What font should be used to draw the text of the tooltip?
		/// </summary>
		public Font Font;
	}

	/// <summary>
	/// Common information to all hyperlink events
	/// </summary>
	public class HyperlinkEventArgs : EventArgs {
		//TODO: Unified with CellEventArgs

		/// <summary>
		/// Gets the FluentListView that is the source of the event
		/// </summary>
		public AdvancedListView ListView {
			get => listView;
			internal set => listView = value;
		}

		private AdvancedListView listView;

		/// <summary>
		/// Gets the model object under the cell
		/// </summary>
		public object Model {
			get => model;
			internal set => model = value;
		}

		private object model;

		/// <summary>
		/// Gets the row index of the cell
		/// </summary>
		public int RowIndex {
			get => rowIndex;
			internal set => rowIndex = value;
		}

		private int rowIndex = -1;

		/// <summary>
		/// Gets the column index of the cell
		/// </summary>
		/// <remarks>This is -1 when the view is not in details view.</remarks>
		public int ColumnIndex {
			get => columnIndex;
			internal set => columnIndex = value;
		}

		private int columnIndex = -1;

		/// <summary>
		/// Gets the column of the cell 
		/// </summary>
		/// <remarks>This is null when the view is not in details view.</remarks>
		public OLVColumn Column {
			get => column;
			internal set => column = value;
		}

		private OLVColumn column;

		/// <summary>
		/// Gets the item of the cell
		/// </summary>
		public OLVListItem Item {
			get => item;
			internal set => item = value;
		}

		private OLVListItem item;

		/// <summary>
		/// Gets the subitem of the cell
		/// </summary>
		/// <remarks>This is null when the view is not in details view</remarks>
		public OLVListSubItem SubItem {
			get => subItem;
			internal set => subItem = value;
		}

		private OLVListSubItem subItem;

		/// <summary>
		/// Gets the FluentListView that is the source of the event
		/// </summary>
		public string Url {
			get => url;
			internal set => url = value;
		}

		private string url;

		/// <summary>
		/// Gets or set if this event completelely handled. If it was, no further processing
		/// will be done for it.
		/// </summary>
		public bool Handled {
			get => handled;
			set => handled = value;
		}

		private bool handled;
	}

	/// <summary>
	/// 
	/// </summary>
	public class IsHyperlinkEventArgs : EventArgs {
		/// <summary>
		/// Gets the FluentListView that is the source of the event
		/// </summary>
		public AdvancedListView ListView {
			get => listView;
			internal set => listView = value;
		}

		private AdvancedListView listView;

		/// <summary>
		/// Gets the model object under the cell
		/// </summary>
		public object Model {
			get => model;
			internal set => model = value;
		}

		private object model;

		/// <summary>
		/// Gets the column of the cell 
		/// </summary>
		/// <remarks>This is null when the view is not in details view.</remarks>
		public OLVColumn Column {
			get => column;
			internal set => column = value;
		}

		private OLVColumn column;

		/// <summary>
		/// Gets the text of the cell 
		/// </summary>
		public string Text {
			get => text;
			internal set => text = value;
		}

		private string text;

		/// <summary>
		/// Gets or sets whether or not this cell is a hyperlink.
		/// Defaults to true for enabled rows and false for disabled rows. 
		/// </summary>
		public bool IsHyperlink {
			get => isHyperlink;
			set => isHyperlink = value;
		}

		private bool isHyperlink;

		/// <summary>
		/// Gets or sets the url that should be invoked when this cell is clicked.
		/// </summary>
		/// <remarks>Setting this to None or String.Empty means that this cell is not a hyperlink</remarks>
		public string Url;
	}

	/// <summary>
	/// </summary>
	public class FormatRowEventArgs : EventArgs {
		//TODO: Unified with CellEventArgs

		/// <summary>
		/// Gets the FluentListView that is the source of the event
		/// </summary>
		public AdvancedListView ListView {
			get => listView;
			internal set => listView = value;
		}

		private AdvancedListView listView;

		/// <summary>
		/// Gets the item of the cell
		/// </summary>
		public OLVListItem Item {
			get => item;
			internal set => item = value;
		}

		private OLVListItem item;

		/// <summary>
		/// Gets the model object under the cell
		/// </summary>
		public object Model => Item.RowObject;

		/// <summary>
		/// Gets the row index of the cell
		/// </summary>
		public int RowIndex {
			get => rowIndex;
			internal set => rowIndex = value;
		}

		private int rowIndex = -1;

		/// <summary>
		/// Gets the display index of the row
		/// </summary>
		public int DisplayIndex {
			get => displayIndex;
			internal set => displayIndex = value;
		}

		private int displayIndex = -1;

		/// <summary>
		/// Should events be triggered for each cell in this row?
		/// </summary>
		public bool UseCellFormatEvents {
			get => useCellFormatEvents;
			set => useCellFormatEvents = value;
		}

		private bool useCellFormatEvents;
	}

	/// <summary>
	/// Parameter block for FormatCellEvent
	/// </summary>
	public class FormatCellEventArgs : FormatRowEventArgs {
		/// <summary>
		/// Gets the column index of the cell
		/// </summary>
		/// <remarks>This is -1 when the view is not in details view.</remarks>
		public int ColumnIndex {
			get => columnIndex;
			internal set => columnIndex = value;
		}

		private int columnIndex = -1;

		/// <summary>
		/// Gets the column of the cell 
		/// </summary>
		/// <remarks>This is null when the view is not in details view.</remarks>
		public OLVColumn Column {
			get => column;
			internal set => column = value;
		}

		private OLVColumn column;

		/// <summary>
		/// Gets the subitem of the cell
		/// </summary>
		/// <remarks>This is null when the view is not in details view</remarks>
		public OLVListSubItem SubItem {
			get => subItem;
			internal set => subItem = value;
		}

		private OLVListSubItem subItem;

		/// <summary>
		/// Gets the model value that is being displayed by the cell.
		/// </summary>
		/// <remarks>This is null when the view is not in details view</remarks>
		public object CellValue => SubItem == null ? null : SubItem.ModelValue;
	}

	/// <summary>
	/// The event args when a hyperlink is clicked
	/// </summary>
	public class HyperlinkClickedEventArgs : CellEventArgs {
		/// <summary>
		/// Gets the url that was associated with this cell.
		/// </summary>
		public string Url {
			get => url;
			set => url = value;
		}

		private string url;
	}

	/// <summary>
	/// The event args when the check box in a column header is changing
	/// </summary>
	public class HeaderCheckBoxChangingEventArgs : CancelEventArgs {
		/// <summary>
		/// Get the column whose checkbox is changing
		/// </summary>
		public OLVColumn Column {
			get => column;
			internal set => column = value;
		}

		private OLVColumn column;

		/// <summary>
		/// Get or set the new state that should be used by the column
		/// </summary>
		public CheckState NewCheckState {
			get => newCheckState;
			set => newCheckState = value;
		}

		private CheckState newCheckState;
	}

	/// <summary>
	/// The event args when the hot item changed
	/// </summary>
	public class HotItemChangedEventArgs : EventArgs {
		/// <summary>
		/// Gets or set if this event completelely handled. If it was, no further processing
		/// will be done for it.
		/// </summary>
		public bool Handled {
			get => handled;
			set => handled = value;
		}

		private bool handled;

		/// <summary>
		/// Gets the part of the cell that the mouse is over
		/// </summary>
		public HitTestLocation HotCellHitLocation {
			get => newHotCellHitLocation;
			internal set => newHotCellHitLocation = value;
		}

		private HitTestLocation newHotCellHitLocation;

		/// <summary>
		/// Gets an extended indication of the part of item/subitem/group that the mouse is currently over
		/// </summary>
		public virtual HitTestLocationEx HotCellHitLocationEx {
			get => hotCellHitLocationEx;
			internal set => hotCellHitLocationEx = value;
		}

		private HitTestLocationEx hotCellHitLocationEx;

		/// <summary>
		/// Gets the index of the column that the mouse is over
		/// </summary>
		/// <remarks>In non-details view, this will always be 0.</remarks>
		public int HotColumnIndex {
			get => newHotColumnIndex;
			internal set => newHotColumnIndex = value;
		}

		private int newHotColumnIndex;

		/// <summary>
		/// Gets the index of the row that the mouse is over
		/// </summary>
		public int HotRowIndex {
			get => newHotRowIndex;
			internal set => newHotRowIndex = value;
		}

		private int newHotRowIndex;

		/// <summary>
		/// Gets the group that the mouse is over
		/// </summary>
		public OLVGroup HotGroup {
			get => hotGroup;
			internal set => hotGroup = value;
		}

		private OLVGroup hotGroup;

		/// <summary>
		/// Gets the part of the cell that the mouse used to be over
		/// </summary>
		public HitTestLocation OldHotCellHitLocation {
			get => oldHotCellHitLocation;
			internal set => oldHotCellHitLocation = value;
		}

		private HitTestLocation oldHotCellHitLocation;

		/// <summary>
		/// Gets an extended indication of the part of item/subitem/group that the mouse used to be over
		/// </summary>
		public virtual HitTestLocationEx OldHotCellHitLocationEx {
			get => oldHotCellHitLocationEx;
			internal set => oldHotCellHitLocationEx = value;
		}

		private HitTestLocationEx oldHotCellHitLocationEx;

		/// <summary>
		/// Gets the index of the column that the mouse used to be over
		/// </summary>
		public int OldHotColumnIndex {
			get => oldHotColumnIndex;
			internal set => oldHotColumnIndex = value;
		}

		private int oldHotColumnIndex;

		/// <summary>
		/// Gets the index of the row that the mouse used to be over
		/// </summary>
		public int OldHotRowIndex {
			get => oldHotRowIndex;
			internal set => oldHotRowIndex = value;
		}

		private int oldHotRowIndex;

		/// <summary>
		/// Gets the group that the mouse used to be over
		/// </summary>
		public OLVGroup OldHotGroup {
			get => oldHotGroup;
			internal set => oldHotGroup = value;
		}

		private OLVGroup oldHotGroup;

		/// <summary>
		/// Returns a string that represents the current object.
		/// </summary>
		/// <returns>
		/// A string that represents the current object.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		public override string ToString() {
			return string.Format("NewHotCellHitLocation: {0}, HotCellHitLocationEx: {1}, NewHotColumnIndex: {2}, NewHotRowIndex: {3}, HotGroup: {4}", newHotCellHitLocation, hotCellHitLocationEx, newHotColumnIndex, newHotRowIndex, hotGroup);
		}
	}

	/// <summary>
	/// Let the world know that a checkbox on a subitem is changing
	/// </summary>
	public class SubItemCheckingEventArgs : CancellableEventArgs {
		/// <summary>
		/// Create a new event block
		/// </summary>
		/// <param name="column"></param>
		/// <param name="item"></param>
		/// <param name="subItemIndex"></param>
		/// <param name="currentValue"></param>
		/// <param name="newValue"></param>
		public SubItemCheckingEventArgs(OLVColumn column, OLVListItem item, int subItemIndex, CheckState currentValue, CheckState newValue) {
			this.column = column;
			listViewItem = item;
			this.subItemIndex = subItemIndex;
			this.currentValue = currentValue;
			this.newValue = newValue;
		}

		/// <summary>
		/// The column of the cell that is having its checkbox changed.
		/// </summary>
		public OLVColumn Column => column;

		private OLVColumn column;

		/// <summary>
		/// The model object of the row of the cell that is having its checkbox changed.
		/// </summary>
		public object RowObject => listViewItem.RowObject;

		/// <summary>
		/// The listview item of the cell that is having its checkbox changed.
		/// </summary>
		public OLVListItem ListViewItem => listViewItem;

		private OLVListItem listViewItem;

		/// <summary>
		/// The current check state of the cell.
		/// </summary>
		public CheckState CurrentValue => currentValue;

		private CheckState currentValue;

		/// <summary>
		/// The proposed new check state of the cell.
		/// </summary>
		public CheckState NewValue {
			get => newValue;
			set => newValue = value;
		}

		private CheckState newValue;

		/// <summary>
		/// The index of the cell that is going to be or has been edited.
		/// </summary>
		public int SubItemIndex => subItemIndex;

		private int subItemIndex;
	}

	/// <summary>
	/// This event argument block is used when groups are created for a list.
	/// </summary>
	public class CreateGroupsEventArgs : EventArgs {
		/// <summary>
		/// Create a CreateGroupsEventArgs
		/// </summary>
		/// <param name="parms"></param>
		public CreateGroupsEventArgs(GroupingParameters parms) {
			parameters = parms;
		}

		/// <summary>
		/// Gets the settings that control the creation of groups
		/// </summary>
		public GroupingParameters Parameters => parameters;

		private GroupingParameters parameters;

		/// <summary>
		/// Gets or sets the groups that should be used
		/// </summary>
		public IList<OLVGroup> Groups {
			get => groups;
			set => groups = value;
		}

		private IList<OLVGroup> groups;

		/// <summary>
		/// Has this event been cancelled by the event handler?
		/// </summary>
		public bool Canceled {
			get => canceled;
			set => canceled = value;
		}

		private bool canceled;
	}

	/// <summary>
	/// This event argument block is used when the text of a group task is clicked
	/// </summary>
	public class GroupTaskClickedEventArgs : EventArgs {
		/// <summary>
		/// Create a GroupTaskClickedEventArgs
		/// </summary>
		/// <param name="group"></param>
		public GroupTaskClickedEventArgs(OLVGroup group) {
			this.group = group;
		}

		/// <summary>
		/// Gets which group was clicked
		/// </summary>
		public OLVGroup Group => group;

		private readonly OLVGroup group;
	}

	/// <summary>
	/// This event argument block is used when a group is about to expand or collapse
	/// </summary>
	public class GroupExpandingCollapsingEventArgs : CancellableEventArgs {
		/// <summary>
		/// Create a GroupExpandingCollapsingEventArgs
		/// </summary>
		/// <param name="group"> </param>
		public GroupExpandingCollapsingEventArgs(OLVGroup group) {
			if (group == null) {
				throw new ArgumentNullException("group");
			}

			olvGroup = group;
		}

		/// <summary>
		/// Gets which group is expanding/collapsing
		/// </summary>
		public OLVGroup Group => olvGroup;

		private readonly OLVGroup olvGroup;

		/// <summary>
		/// Gets whether this event is going to expand the group.
		/// If this is false, the group must be collapsing.
		/// </summary>
		public bool IsExpanding => Group.Collapsed;
	}

	/// <summary>
	/// This event argument block is used when the state of group has changed (collapsed, selected)
	/// </summary>
	public class GroupStateChangedEventArgs : EventArgs {
		/// <summary>
		/// Create a GroupStateChangedEventArgs
		/// </summary>
		/// <param name="group"></param>
		/// <param name="oldState"> </param>
		/// <param name="newState"> </param>
		public GroupStateChangedEventArgs(OLVGroup group, GroupState oldState, GroupState newState) {
			this.group = group;
			this.oldState = oldState;
			this.newState = newState;
		}

		/// <summary>
		/// Gets whether the group was collapsed by this event
		/// </summary>
		public bool Collapsed =>
			(oldState & GroupState.LVGS_COLLAPSED) != GroupState.LVGS_COLLAPSED &&
			(newState & GroupState.LVGS_COLLAPSED) == GroupState.LVGS_COLLAPSED;

		/// <summary>
		/// Gets whether the group was focused by this event
		/// </summary>
		public bool Focused =>
			(oldState & GroupState.LVGS_FOCUSED) != GroupState.LVGS_FOCUSED &&
			(newState & GroupState.LVGS_FOCUSED) == GroupState.LVGS_FOCUSED;

		/// <summary>
		/// Gets whether the group was selected by this event
		/// </summary>
		public bool Selected =>
			(oldState & GroupState.LVGS_SELECTED) != GroupState.LVGS_SELECTED &&
			(newState & GroupState.LVGS_SELECTED) == GroupState.LVGS_SELECTED;

		/// <summary>
		/// Gets whether the group was uncollapsed by this event
		/// </summary>
		public bool Uncollapsed =>
			(oldState & GroupState.LVGS_COLLAPSED) == GroupState.LVGS_COLLAPSED &&
			(newState & GroupState.LVGS_COLLAPSED) != GroupState.LVGS_COLLAPSED;

		/// <summary>
		/// Gets whether the group was unfocused by this event
		/// </summary>
		public bool Unfocused =>
			(oldState & GroupState.LVGS_FOCUSED) == GroupState.LVGS_FOCUSED &&
			(newState & GroupState.LVGS_FOCUSED) != GroupState.LVGS_FOCUSED;

		/// <summary>
		/// Gets whether the group was unselected by this event
		/// </summary>
		public bool Unselected =>
			(oldState & GroupState.LVGS_SELECTED) == GroupState.LVGS_SELECTED &&
			(newState & GroupState.LVGS_SELECTED) != GroupState.LVGS_SELECTED;

		/// <summary>
		/// Gets which group had its state changed
		/// </summary>
		public OLVGroup Group => group;

		private readonly OLVGroup group;

		/// <summary>
		/// Gets the previous state of the group
		/// </summary>
		public GroupState OldState => oldState;

		private readonly GroupState oldState;


		/// <summary>
		/// Gets the new state of the group
		/// </summary>
		public GroupState NewState => newState;

		private readonly GroupState newState;
	}

	/// <summary>
	/// This event argument block is used when a branch of a tree is about to be expanded
	/// </summary>
	public class TreeBranchExpandingEventArgs : CancellableEventArgs {
		/// <summary>
		/// Create a new event args
		/// </summary>
		/// <param name="model"></param>
		/// <param name="item"></param>
		public TreeBranchExpandingEventArgs(object model, OLVListItem item) {
			Model = model;
			Item = item;
		}

		/// <summary>
		/// Gets the model that is about to expand. If null, all branches are going to be expanded.
		/// </summary>
		public object Model {
			get => model;
			private set => model = value;
		}

		private object model;

		/// <summary>
		/// Gets the OLVListItem that is about to be expanded
		/// </summary>
		public OLVListItem Item {
			get => item;
			private set => item = value;
		}

		private OLVListItem item;
	}

	/// <summary>
	/// This event argument block is used when a branch of a tree has just been expanded
	/// </summary>
	public class TreeBranchExpandedEventArgs : EventArgs {
		/// <summary>
		/// Create a new event args
		/// </summary>
		/// <param name="model"></param>
		/// <param name="item"></param>
		public TreeBranchExpandedEventArgs(object model, OLVListItem item) {
			Model = model;
			Item = item;
		}

		/// <summary>
		/// Gets the model that is was expanded. If null, all branches were expanded.
		/// </summary>
		public object Model {
			get => model;
			private set => model = value;
		}

		private object model;

		/// <summary>
		/// Gets the OLVListItem that was expanded
		/// </summary>
		public OLVListItem Item {
			get => item;
			private set => item = value;
		}

		private OLVListItem item;
	}

	/// <summary>
	/// This event argument block is used when a branch of a tree is about to be collapsed
	/// </summary>
	public class TreeBranchCollapsingEventArgs : CancellableEventArgs {
		/// <summary>
		/// Create a new event args
		/// </summary>
		/// <param name="model"></param>
		/// <param name="item"></param>
		public TreeBranchCollapsingEventArgs(object model, OLVListItem item) {
			Model = model;
			Item = item;
		}

		/// <summary>
		/// Gets the model that is about to collapse. If this is null, all models are going to collapse.
		/// </summary>
		public object Model {
			get => model;
			private set => model = value;
		}

		private object model;

		/// <summary>
		/// Gets the OLVListItem that is about to be collapsed. Can be null
		/// </summary>
		public OLVListItem Item {
			get => item;
			private set => item = value;
		}

		private OLVListItem item;
	}


	/// <summary>
	/// This event argument block is used when a branch of a tree has just been collapsed
	/// </summary>
	public class TreeBranchCollapsedEventArgs : EventArgs {
		/// <summary>
		/// Create a new event args
		/// </summary>
		/// <param name="model"></param>
		/// <param name="item"></param>
		public TreeBranchCollapsedEventArgs(object model, OLVListItem item) {
			Model = model;
			Item = item;
		}

		/// <summary>
		/// Gets the model that is was collapsed. If null, all branches were collapsed
		/// </summary>
		public object Model {
			get => model;
			private set => model = value;
		}

		private object model;

		/// <summary>
		/// Gets the OLVListItem that was collapsed
		/// </summary>
		public OLVListItem Item {
			get => item;
			private set => item = value;
		}

		private OLVListItem item;
	}

	#endregion
}